home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / tag.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  12KB  |  495 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8.  
  9. /*
  10.  * Code to handle tags and the tag stack
  11.  */
  12.  
  13. #include "vim.h"
  14. #include "globals.h"
  15. #include "proto.h"
  16. #include "param.h"
  17.  
  18. static int findtag __ARGS((char_u *));
  19. static char_u *bottommsg = (char_u *)"at bottom of tag stack";
  20. static char_u *topmsg = (char_u *)"at top of tag stack";
  21.  
  22. /*
  23.  * Jump to tag; handling of tag stack
  24.  *
  25.  * *tag != NUL (:tag): jump to new tag, add to tag stack
  26.  * type == 1 (:pop) || type == 2 (CTRL-T): jump to old position
  27.  * type == 0 (:tag): jump to old tag
  28.  */
  29.     void
  30. dotag(tag, type, count)
  31.     char_u    *tag;
  32.     int        type;
  33.     int        count;
  34. {
  35.     int             i;
  36.     struct taggy    *tagstack = curwin->w_tagstack;
  37.     int                tagstackidx = curwin->w_tagstackidx;
  38.     int                tagstacklen = curwin->w_tagstacklen;
  39.  
  40.     if (*tag != NUL)                        /* new pattern, add to the stack */
  41.     {
  42.         /*
  43.          * if last used entry is not at the top, delete all tag stack entries
  44.          * above it.
  45.          */
  46.         while (tagstackidx < tagstacklen)
  47.             free(tagstack[--tagstacklen].tagname);
  48.  
  49.                 /* if tagstack is full: remove oldest entry */
  50.         if (++tagstacklen > TAGSTACKSIZE)
  51.         {
  52.             tagstacklen = TAGSTACKSIZE;
  53.             free(tagstack[0].tagname);
  54.             for (i = 1; i < tagstacklen; ++i)
  55.                 tagstack[i - 1] = tagstack[i];
  56.             --tagstackidx;
  57.         }
  58.     /*
  59.      * put the tag name in the tag stack
  60.      * the position is added below
  61.      */
  62.         tagstack[tagstackidx].tagname = strsave(tag);
  63.     }
  64.     else if (tagstacklen == 0)                    /* empty stack */
  65.     {
  66.         EMSG("tag stack empty");
  67.         goto end_dotag;
  68.     }
  69.     else if (type)                                /* go to older position */
  70.     {
  71.         if ((tagstackidx -= count) < 0)
  72.         {
  73.             emsg(bottommsg);
  74.             if (tagstackidx + count == 0)
  75.             {
  76.                 /* We did ^T (or <num>^T) from the bottom of the stack */
  77.                 tagstackidx = 0;
  78.                 goto end_dotag;
  79.             }
  80.             /* We weren't at the bottom of the stack, so jump all the way to
  81.              * the bottom.
  82.              */
  83.             tagstackidx = 0;
  84.         }
  85.         else if (tagstackidx >= tagstacklen)    /* must have been count == 0 */
  86.         {
  87.             emsg(topmsg);
  88.             goto end_dotag;
  89.         }
  90.         if (tagstack[tagstackidx].fmark.fnum != curbuf->b_fnum)    /* jump to other file */
  91.         {
  92.             if (buflist_getfile(tagstack[tagstackidx].fmark.fnum, tagstack[tagstackidx].fmark.mark.lnum, TRUE) == FAIL)
  93.             {
  94.                 /* emsg(e_notopen); */
  95.                 goto end_dotag;
  96.             }
  97.         }
  98.         else
  99.             curwin->w_cursor.lnum = tagstack[tagstackidx].fmark.mark.lnum;
  100.         curwin->w_cursor.col = tagstack[tagstackidx].fmark.mark.col;
  101.         curwin->w_set_curswant = TRUE;
  102.         goto end_dotag;
  103.     }
  104.     else                                    /* go to newer pattern */
  105.     {
  106.         if ((tagstackidx += count - 1) >= tagstacklen)
  107.         {
  108.             tagstackidx = tagstacklen - 1;
  109.             emsg(topmsg);
  110.         }
  111.         else if (tagstackidx < 0)            /* must have been count == 0 */
  112.         {
  113.             emsg(bottommsg);
  114.             tagstackidx = 0;
  115.             goto end_dotag;
  116.         }
  117.     }
  118.     /*
  119.      * For :tag [arg], remember position before the jump
  120.      */
  121.     if (type == 0)
  122.     {
  123.         tagstack[tagstackidx].fmark.mark = curwin->w_cursor;
  124.         tagstack[tagstackidx].fmark.fnum = curbuf->b_fnum;
  125.     }
  126.     if (findtag(tagstack[tagstackidx].tagname) > 0)
  127.         ++tagstackidx;
  128.  
  129. end_dotag:
  130.     curwin->w_tagstackidx = tagstackidx;
  131.     curwin->w_tagstacklen = tagstacklen;
  132. }
  133.  
  134. /*
  135.  * Print the tag stack
  136.  */
  137.     void
  138. dotags()
  139. {
  140.     int                i;
  141.     char_u            *name;
  142.     struct taggy    *tagstack = curwin->w_tagstack;
  143.     int                tagstackidx = curwin->w_tagstackidx;
  144.     int                tagstacklen = curwin->w_tagstacklen;
  145.  
  146.     gotocmdline(TRUE, NUL);
  147.     msg_outstr((char_u *)"\n  # TO tag      FROM line in file\n");
  148.     for (i = 0; i < tagstacklen; ++i)
  149.     {
  150.         if (tagstack[i].tagname != NULL)
  151.         {
  152.             name = fm_getname(&(tagstack[i].fmark));
  153.             if (name == NULL)        /* file name not available */
  154.                 continue;
  155.  
  156.             sprintf((char *)IObuff, "%c%2d %-15s %4ld  %s\n",
  157.                 i == tagstackidx ? '>' : ' ',
  158.                 i + 1,
  159.                 tagstack[i].tagname,
  160.                 tagstack[i].fmark.mark.lnum,
  161.                 name);
  162.             msg_outstr(IObuff);
  163.         }
  164.         flushbuf();                    /* show one line at a time */
  165.     }
  166.     if (tagstackidx == tagstacklen)        /* idx at top of stack */
  167.         msg_outstr((char_u *)">\n");
  168.     wait_return(FALSE);
  169. }
  170.  
  171. /*
  172.  * findtag(tag) - goto tag
  173.  *   return 0 for failure, 1 for success
  174.  */
  175.     static int
  176. findtag(tag)
  177.     char_u           *tag;
  178. {
  179.     FILE       *tp;
  180.     char_u        lbuf[LSIZE];
  181.     char_u        pbuf[LSIZE];            /* search pattern buffer */
  182.     char_u       *fname, *str;
  183.     int            cmplen;
  184.     char_u        *m = (char_u *)"No tags file";
  185.     char_u        *marg = NULL;
  186.     register char_u    *p;
  187.     char_u        *p2;
  188.     char_u        *np;                    /* pointer into file name string */
  189.     char_u        sbuf[CMDBUFFSIZE + 1];    /* tag file name */
  190.     int            i;
  191.     int            save_secure;
  192.     int            save_p_ws;
  193.  
  194.     if (tag == NULL)        /* out of memory condition */
  195.         return 0;
  196.  
  197.     if ((cmplen = p_tl) == 0)
  198.         cmplen = 999;
  199.  
  200.     /* get stack of tag file names from tags option */
  201.     for (np = p_tags; *np; )
  202.     {
  203.         for (i = 0; i < CMDBUFFSIZE && *np; ++i)    /* copy next file name into lbuf */
  204.         {
  205.             if (*np == ' ')
  206.             {
  207.                 ++np;
  208.                 break;
  209.             }
  210.             sbuf[i] = *np++;
  211.         }
  212.         sbuf[i] = 0;
  213.         if ((tp = fopen((char *)sbuf, "r")) == NULL)
  214.             continue;
  215.         reg_ic = p_ic;                                        /* for cstrncmp() */
  216.         while (fgets((char *)lbuf, LSIZE, tp) != NULL)
  217.         {
  218.             m = (char_u *)"Format error in tags file %s";    /* default error message */
  219.             marg = sbuf;
  220.  
  221.         /* find start of file name, after first white space */
  222.             fname = lbuf;
  223.             skiptospace(&fname);    /* skip tag */
  224.             if (*fname == NUL)
  225.                 goto erret;
  226.             *fname++ = '\0';
  227.  
  228.             if (cstrncmp(lbuf, tag, cmplen) == 0)    /* Tag found */
  229.             {
  230.                 fclose(tp);
  231.                 skipspace(&fname);
  232.  
  233.             /* find start of search command, after second white space */
  234.                 str = fname;
  235.                 skiptospace(&str);
  236.                 if (*str == NUL)
  237.                     goto erret;
  238.                 *str++ = '\0';
  239.                 skipspace(&str);
  240.  
  241.                 /*
  242.                  * If the command is a string like "/^function fname"
  243.                  * scan through the search string. If we see a magic
  244.                  * char, we have to quote it. This lets us use "real"
  245.                  * implementations of ctags.
  246.                  */
  247.                 if (*str == '/' || *str == '?')
  248.                 {
  249.                     p = pbuf;
  250.                     *p++ = *str++;            /* copy the '/' or '?' */
  251.                     if (*str == '^')
  252.                         *p++ = *str++;            /* copy the '^' */
  253.  
  254.                     while (*str)
  255.                     {
  256.                         switch (*str)
  257.                         {
  258.                         case '\\':    if (str[1] == '(')    /* remove '\' before '(' */
  259.                                         ++str;
  260.                                     else
  261.                                         *p++ = *str++;
  262.                                     break;
  263.  
  264.                         case '\r':
  265.                         case '\n':    *str = pbuf[0];    /* copy '/' or '?' */
  266.                                     str[1] = NUL;    /* delete NL after CR */
  267.                                     break;
  268.  
  269.                                     /*
  270.                                      * if string ends in search character: skip it
  271.                                      * else escape it with '\'
  272.                                      */
  273.                         case '/':
  274.                         case '?':    if (*str != pbuf[0])    /* not the search char */
  275.                                         break;
  276.                                                             /* last char */
  277.                                     if (str[1] == '\n' || str[1] == '\r')
  278.                                     {
  279.                                         ++str;
  280.                                         continue;
  281.                                     }
  282.                         case '[':
  283.                                     if (!p_magic)
  284.                                         break;
  285.                         case '^':
  286.                         case '*':
  287.                         case '.':    *p++ = '\\';
  288.                                     break;
  289.                         }
  290.                         *p++ = *str++;
  291.                     }
  292.                 }
  293.                 else        /* not a search command, just copy it */
  294.                     for (p = pbuf; *str && *str != '\n'; )
  295.                         *p++ = *str++;
  296.                 *p = NUL;
  297.  
  298.                 /*
  299.                  * expand filename (for environment variables)
  300.                  */
  301.                 if ((p = ExpandOne((char_u *)fname, 1, -1)) != NULL)
  302.                     fname = p;
  303.                 /*
  304.                  * if 'tagrelative' option set, may change file name
  305.                  */
  306.                 if (p_tr && !isFullName(fname) && (p2 = gettail(sbuf)) != sbuf)
  307.                 {
  308.                     STRNCPY(p2, fname, CMDBUFFSIZE - (p2 - sbuf));
  309.                     fname = sbuf;
  310.                 }
  311.                 /*
  312.                  * check if file for tag exists before abandoning current file
  313.                  */
  314.                 if (getperm(fname) < 0)
  315.                 {
  316.                     m = (char_u *)"File \"%s\" does not exist";
  317.                     marg = fname;
  318.                     goto erret;
  319.                 }
  320.  
  321.                 RedrawingDisabled = TRUE;
  322.                 /*
  323.                  * if it was a CTRL-W CTRL-] command split window now
  324.                  */
  325.                 if (postponed_split)
  326.                     win_split(0L, FALSE);
  327.                 i = getfile(fname, NULL, TRUE, (linenr_t)0);
  328.                 if (p)
  329.                     free(p);
  330.                 if (i <= 0)
  331.                 {
  332.                     curwin->w_set_curswant = TRUE;
  333.                     postponed_split = FALSE;
  334.  
  335.                     RedrawingDisabled = FALSE;
  336.                     save_secure = secure;
  337.                     secure = 1;
  338.                     tag_busy = TRUE;            /* don't set marks for this search */
  339.                     keep_old_search_pattern = TRUE;
  340.  
  341.                     /*
  342.                      * if the command is a search, try here
  343.                      *
  344.                      * Rather than starting at line one, just turn wrap-scan
  345.                      * on temporarily, this ensures that tags on line 1 will
  346.                      * be found, and makes sure our guess searches search the
  347.                      * whole file when repeated -- webb.
  348.                      */
  349.                     if (pbuf[0] == '/' || pbuf[0] == '?')
  350.                     {
  351.                         save_p_ws = p_ws;
  352.                         p_ws = TRUE;        /* Switch wrap-scan on temporarily */
  353.                         if (!dosearch(pbuf[0], pbuf + 1, FALSE, (long)1, FALSE, FALSE))
  354.                         {
  355.                             register int notfound = FALSE;
  356.  
  357.                             /*
  358.                              * Failed to find pattern, take a guess:
  359.                              */
  360.                             sprintf((char *)pbuf, "^%s(", lbuf);
  361.                             if (!dosearch('/', pbuf, FALSE, (long)1, FALSE, FALSE))
  362.                             {
  363.                                 /* Guess again: */
  364.                                 sprintf((char *)pbuf, "^[#a-zA-Z_].*%s(", lbuf);
  365.                                 if (!dosearch('/', pbuf, FALSE, (long)1, FALSE, FALSE))
  366.                                     notfound = TRUE;
  367.                             }
  368.                             if (notfound)
  369.                                 EMSG("Can't find tag pattern");
  370.                             else
  371.                             {
  372.                                 MSG("Couldn't find tag, just guessing!");
  373.                                 sleep(1);
  374.                             }
  375.                         }
  376.                         p_ws = save_p_ws;
  377.                     }
  378.                     else
  379.                     {
  380.                         curwin->w_cursor.lnum = 1;    /* start command in line 1 */
  381.                         docmdline(pbuf);
  382.                     }
  383.  
  384.                     tag_busy = FALSE;
  385.                     keep_old_search_pattern = FALSE;
  386.                     if (secure == 2)            /* done something that is not allowed */
  387.                         wait_return(TRUE);
  388.                     secure = save_secure;
  389.  
  390.                         /* print the file message after redraw */
  391.                     if (p_im && i == -1)
  392.                         stuffReadbuff((char_u *)"\033\007i");    /* ESC CTRL-G i */
  393.                     else
  394.                         stuffcharReadbuff('\007');        /* CTRL-G */
  395.                     return 1;
  396.                 }
  397.                 RedrawingDisabled = FALSE;
  398.                 if (postponed_split)            /* close the window */
  399.                 {
  400.                     close_window(FALSE);
  401.                     postponed_split = FALSE;
  402.                 }
  403.                 return 0;
  404.             }
  405.         }
  406.         m = NULL;
  407.  
  408. erret:
  409.         fclose(tp);
  410.         if (m)
  411.             emsg2(m, marg);
  412.     }
  413.     if (m == NULL)
  414.         EMSG("tag not found");
  415.     else if (marg == NULL)
  416.         emsg(m);
  417.     return 0;
  418. }
  419.  
  420. #ifdef WEBB_COMPLETE
  421.     int
  422. ExpandTags(prog, num_file, file)
  423.     regexp *prog;
  424.     int *num_file;
  425.     char_u ***file;
  426. {
  427.     char_u    **matches, **new_matches;
  428.     char_u    tag_file[CMDBUFFSIZE + 1];
  429.     char_u    line[LSIZE];
  430.     char_u    *np;
  431.     char_u    *p;
  432.     int        limit = 100;
  433.     int        index;
  434.     int        i;
  435.     int        lnum;
  436.     FILE    *fp;
  437.  
  438.     matches = (char_u **) alloc((unsigned)(limit * sizeof(char_u *)));
  439.     if (matches == NULL)
  440.         return FAIL;
  441.     index = 0;
  442.     for (np = p_tags; *np; )
  443.     {
  444.         for (i = 0; i < CMDBUFFSIZE && *np && *np != ' '; i++)
  445.             tag_file[i] = *np++;
  446.         tag_file[i] = NUL;
  447.         skipspace(&np);
  448.         if ((fp = fopen((char *)tag_file, "r")) == NULL)
  449.             continue;
  450.         lnum = 0;
  451.         while (!vim_fgets(line, LSIZE, fp, &lnum))
  452.         {
  453.             if (regexec(prog, line, TRUE))
  454.             {
  455.                 p = line;
  456.                 skiptospace(&p);
  457.                 *p = NUL;
  458.                 if (index == limit)
  459.                 {
  460.                     limit *= 2;
  461.                     new_matches = (char_u **) alloc((unsigned)(limit * sizeof(char_u *)));
  462.                     if (new_matches == NULL)
  463.                     {
  464.                         /* We'll miss some matches, oh well */
  465.                         *file = matches;
  466.                         *num_file = index;
  467.                         return OK;
  468.                     }
  469.                     for (i = 0; i < index; i++)
  470.                         new_matches[i] = matches[i];
  471.                     free(matches);
  472.                     matches = new_matches;
  473.                 }
  474.                 matches[index++] = strsave(line);
  475.             }
  476.         }
  477.     }
  478.     if (index > 0)
  479.     {
  480.         new_matches = *file = (char_u **) alloc((unsigned)(index * sizeof(char_u *)));
  481.         if (new_matches == NULL)
  482.         {
  483.             *file = matches;
  484.             *num_file = index;
  485.             return OK;
  486.         }
  487.         for (i = 0; i < index; i++)
  488.             new_matches[i] = matches[i];
  489.     }
  490.     free(matches);
  491.     *num_file = index;
  492.     return OK;
  493. }
  494. #endif /* WEBB_COMPLETE */
  495.